home *** CD-ROM | disk | FTP | other *** search
- /* ran.c - functions to initialize random number generator and random number
- ** generator itself.
- **
- ** Module entry points
- ** -------------------
- **
- ** setup() ----> N.B. MUST be called before irand() or poiss()
- ** ran() ----> macro defined in ran.h
- ** irand()
- ** poiss()
- ** binom()
- **
- **
- ** Either RAN_KNU or RAN_MAR must be defined before compilation, but
- ** only one can be defined. If RAN_KNU is defined, a subtractive
- ** generator from volume two of Knuth is used. If RAN_MAR is defined,
- ** a more complex generator discovered by Marsaglia is used. Knuth's
- ** generator is about 50% faster than Marsaglia's, but its sequence is
- ** not as random. For many purposes, Knuth's generator is good enough.
- ** It is much better than the generators normally supplied with C compilers,
- ** and it is often faster.
- **
- ** setup() must be called before irand() is called. irand() does
- ** NOT check to see that the array it uses has been initialized.
- **
- ** irand() returns a uniformly distributed integer in [0..NO_NUMBERS)
- ** when NO_ZERO is not defined and in (0..NO_NUMBERS) when NO_ZERO is
- ** defined.
- **
- ** The macro ran() (defined in ran.h) returns a uniformly distributed
- ** random number in the range [0,1) when NO_ZERO is not defined and in
- ** (0,1) when NO_ZERO is defined. The resolution is 1/CONV_REAL.
- **
- ** Strictly speaking poiss() should only be called when NO_ZERO is not
- ** defined. But the error will be very small, since it will have an
- ** effect on the results in only 1 out of NO_NUMBERS calls, on average.
- **
- **
- ** References:
- **
- ** Knuth, D. E. 1981. The Art of Computer Programming. Vol. 2,
- ** Seminumerical Algorithms. Addison-Wesley, Reading Mass.
- **
- **
- ** Author: Kent E. Holsinger
- **
- ** Revision history
- ** ----------------
- **
- ** Version 1.0 -- 24 January 1990
- ** Original version of Knuth additive random number generator
- **
- ** Version 1.1 -- 24 January 1990
- ** ran() changed to macro operating on long
- **
- ** Version 2.1 -- 31 December 1990
- ** Added Marsaglia generator with conditional compilation
- **
- ** Version 2.3 -- 18 December 1991
- ** Converted both generators to use unsigned longs
- ** Editorial changes to internal comments
- **
- **
- */
-
- #include <stdio.h>
- #include <math.h>
- #include "ran.h"
-
-
- /* constants used in random number generator */
- #ifdef RAN_KNU
- #define D 21
- #define LASTDIM 55
- #define FIRSTDIM 24
- #endif
- #ifdef RAN_MAR
- #define CD 7654321
- #define CM 16777213
- #define LASTDIM 98
- #define FIRSTDIM 34
- #endif
-
- /*
- ** array of integers used in random number
- ** generator
- */
- typedef unsigned long INTARRAY[LASTDIM];
- typedef unsigned long *POINTER;
-
- /* file used to store random number seed */
- FILE *randfile;
-
- /* LOCAL VARIABLE DEFINITIONS */
- static INTARRAY x; /* array used to generate random nos. */
- static POINTER j_ptr, k_ptr; /* pointers into array */
-
- #ifdef RAN_MAR
- static long c;
- #endif
- static char MOD_ID[] = "%W% %D%";
-
-
- /* LOCAL FUNCTION PROTOTYPES */
- #ifdef __STDC__
- static void rknuin(unsigned long m);
- static void rmarin(int ij, int kl);
- #else
- static void rknuin();
- static void rmarin();
- #endif
-
-
-
- /***************************************************************************/
- /* */
- /* MODULE ENTRY POINT */
- /* setup() */
- /* */
- /***************************************************************************/
- /* Initialize the random number generator */
- #ifdef __STDC__
- void setup(unsigned long *seed)
- #else
- void setup(seed)
- unsigned long *seed;
- #endif
- {
- #ifdef RAN_MAR
- unsigned long temp; /* to write seed to file for next call */
- int ij, kl; /* seeds for Marsaglia generator */
- #endif
-
- randfile = fopen("RAND.INT", "r");
- #ifdef RAN_KNU
- if (!randfile) {
- printf("Enter a random number seed: ");
- scanf("%lu", seed);
- } else {
- fscanf(randfile, "%lu", seed);
- fclose(randfile);
- }
- do {
- if (*seed == 0) {
- fprintf(stderr, "Seed must be greater than zero\n");
- fprintf(stderr, "Enter a random number seed: ");
- scanf("%lu", seed);
- }
- } while (*seed == 0);
- rknuin(*seed);
- randfile = fopen("RAND.INT", "w");
- fprintf(randfile, "%lu", x[LASTDIM - 1]);
- fclose(randfile);
- #endif
- #ifdef RAN_MAR
- if (!randfile) {
- printf("Enter first random number seed: ");
- scanf("%d", &ij);
- printf("Enter second random number seed: ");
- scanf("%d", &kl);
- } else {
- fscanf(randfile, "%lu", seed);
- fclose(randfile);
- ij = (int) (*seed) % 31328;
- kl = (int) ((*seed >> 16) % 30081);
- }
- if (ij < 0 || ij > 31328 || kl < 0 || kl > 30081) {
- do {
- fputs("The first random number seed must have a value between 0\
- and 31328.\n", stderr);
- fputs("The second seed must have a value between 0 and 30081.\n",
- stderr);
- printf("Enter first random number seed: ");
- scanf("%d", &ij);
- printf("Enter second random number seed: ");
- scanf("%d", &kl);
- } while (ij < 0 || ij > 31328 || kl < 0 || kl > 30081);
- }
- rmarin(ij, kl);
- temp = ((x[LASTDIM - 1] >> 16) % 30081) << 16;
- temp += x[LASTDIM - 1] % 31328;
- randfile = fopen("RAND.INT", "w");
- fprintf(randfile, "%lu", temp);
- fclose(randfile);
- #endif
- }
-
-
-
- /***************************************************************************/
- /* */
- /* MODULE ENTRY POINT */
- /* irand() */
- /* */
- /***************************************************************************/
- /* Returns a random number in [0,NO_NUMBERS) */
- /* if NO_ZERO left undefined */
- /* Returns a random number in (0,NO_NUMBERS) */
- /* when NO_ZERO is defined */
- #ifdef RAN_KNU
- /* Knuth generator */
- /*
- * This is a direct translation of the subtractive generator (p. 171),
- * which is based on the additive generator described on p. 27. It has
- * a period greater than 2^55 - 1.
- *
- * This implementation takes advantage of the fact that all elements of
- * the array are themselves random numbers to return the element in the
- * array currently pointed to. Strict implementation of the algorithm
- * would require definition of a temporary variable to hold the return
- * value, which properly is *j_ptr before it is decremented
- *
- * On a Zenith Z-386 each call requires about 13 microseconds with
- * NO_ZERO defined. Without NO_ZERO defined each call requires about
- * 11 microseconds. (Results obtained using Microsoft C v6.00a,
- * optimizing with /Ozx
- */
- #ifdef __STDC__
- unsigned long irand(void)
- #else
- unsigned long irand()
- #endif
- {
- *j_ptr -= *k_ptr;
- j_ptr--;
- k_ptr--;
- if (j_ptr < x)
- j_ptr = &x[LASTDIM - 1];
- else if (k_ptr < x)
- k_ptr = &x[LASTDIM - 1];
- #ifndef NO_ZERO
- return *j_ptr; /* simply return *j_ptr for U in [0,1) */
- #else
- if (*j_ptr == 0)
- return irand();
- else
- return *j_ptr;
- #endif
- }
- #endif
-
- #ifdef RAN_MAR
- /* Marsaglia generator */
- /*
- * C This random number generator originally appeared in "Toward a Universal
- * C Random Number Generator" by George Marsaglia and Arif Zaman. C Florida
- * State University Report: FSU-SCRI-87-50 (1987)
- * C
- * C It was later modified by F. James and published in "A Review of Pseudo-
- * C random Number Generators"
- * C
- * C THIS IS THE BEST KNOWN RANDOM NUMBER GENERATOR AVAILABLE.
- * C (However, a newly discovered technique can yield
- * C a period of 10^600. But that is still in the development stage.)
- * C
- * C It passes ALL of the tests for random number generators and has a period
- * C of 2^144, is completely portable (gives bit identical results on all
- * C machines with at least 24-bit mantissas in the floating point
- * C representation).
- * C
- * C The algorithm is a combination of a Fibonacci sequence (with lags of 97
- * C and 33, and operation "subtraction plus one, modulo one") and an
- * C "arithmetic sequence" (using subtraction).
- * C========================================================================
- * This version is based on a C version by Jim Butler, which is itself based
- * on a FORTRAN program posted by David LaSalle of Florida State University.
- *
- * This version takes advantage of the 32-bit long integers in ANSI C to gain a
- * significant speed increase. On a Zenith Z-386 running DOS 3.2 the
- * floating point version requires about 74 microseconds per call. This
- * version requires about 19 microseconds per call without NO_ZERO defined.
- * With NO_ZERO defined each call requires about 21 microseconds. (Results
- * obtained using Microsoft C v6.00a, optimizing with /Ozx.)
- */
- #ifdef __STDC__
- unsigned long irand(void)
- #else
- unsigned long irand()
- #endif
- {
- long uni;
-
- uni = *j_ptr - *k_ptr;
- if (uni < 0) {
- uni += NO_NUMBERS;
- }
- *j_ptr = uni;
- j_ptr--;
- if (j_ptr == x) {
- j_ptr = &x[LASTDIM - 1];
- }
- k_ptr--;
- if (k_ptr == x) {
- k_ptr = &x[LASTDIM - 1];
- }
- c -= CD;
- if (c < 0) {
- c += CM;
- }
- uni -= c;
- if (uni < 0) {
- uni += NO_NUMBERS;
- }
- #ifndef NO_ZERO
- return (unsigned long) uni; /* simply return uni for U in [0,1) */
- #else
- if (uni == 0)
- return irand();
- else
- return (unsigned long) uni;
- #endif
- }
- #endif
-
-
- /***************************************************************************/
- /* */
- /* MODULE ENTRY POINT */
- /* poiss() */
- /* */
- /***************************************************************************/
- #ifdef __STDC__
- int poiss(double mean)
- #else
- int poiss(mean)
- double mean;
- #endif
- {
- double e_u;
- double u;
- int k;
-
- e_u = exp(-mean);
- u = ran();
- for (k = 1; u > e_u; k++) {
- u *= ran();
- }
- return (k - 1);
- }
-
-
-
- /***************************************************************************/
- /* */
- /* MODULE ENTRY POINT */
- /* binom() */
- /* */
- /***************************************************************************/
- #ifdef __STDC__
- int binom(int n, double p)
- #else
- int binom(n, p)
- int n;
- double p;
- #endif
- {
- int i; /* index variable */
- int ct; /* cumulative number of hits in binomial */
-
- ct = 0;
- for (i = 0; i < n; i++) {
- if (ran() < p) {
- ct++;
- }
- }
- return ct;
- }
-
-
-
-
-
- /***************************************************************************/
- /* */
- /* LOCAL FUNCTIONS */
- /* rknuin() */
- /* rmarin() */
- /* */
- /***************************************************************************/
- #ifdef RAN_KNU
- /* initializer for Knuth generator */
- /* It is based on the one suggested on */
- /* p. 172 */
- #ifdef __STDC__
- static void rknuin(unsigned long m)
- #else
- static void rknuin(m)
- unsigned long m;
- #endif
- {
- double dummy; /* dummy used to "warm up" generator */
- unsigned long int n; /* integers used to load generator */
- int i; /* index variable */
- int item; /* item storage */
-
- x[LASTDIM - 1] = m;
- n = 1;
- for (i = 1; i < LASTDIM; ++i) {
- item = ((D * i) % LASTDIM) - 1;
- x[item] = n;
- n = m - n;
- if (n < 0)
- n += NO_NUMBERS;
- m = x[item];
- }
- j_ptr = &x[FIRSTDIM - 1];
- k_ptr = &x[LASTDIM - 1];
- for (i = 0; i < LASTDIM; ++i)
- dummy = ran();
- }
- #endif
-
- #ifdef RAN_MAR
- /* initializer for Marsaglia generator */
- /*
- * C This is the initialization routine for the random number generator RANMAR()
- * C NOTE: The seed variables can have values between: 0 <= IJ <= 31328
- * C 0 <= KL <= 30081
- * C The random number sequences created by these two seeds are of sufficient
- * C length to complete an entire calculation with. For example, if several
- * C different groups are working on different parts of the same calculation,
- * C each group could be assigned its own IJ seed. This would leave each group
- * C with 30000 choices for the second seed. That is to say, this random
- * C number generator can create 900 million different subsequences -- with
- * C each subsequence having a length of approximately 10^30.
- * C
- * C Use IJ = 1802 & KL = 9373 to test the random number generator. The
- * C subroutine RANMAR should be used to generate 20000 random numbers.
- * C Then display the next six random numbers generated multiplied by 4096*4096
- * C If the random number generator is working properly, the random numbers
- * C should be:
- * C 6533892.0 14220222.0 7275067.0
- * C 6172232.0 8354498.0 10633180.0
- */
- #ifdef __STDC__
- static void rmarin(int ij, int kl)
- #else
- static void rmarin(ij, kl)
- int ij;
- int kl;
- #endif
- {
- int i, j, k, l, ii, jj, m;
- float s, t;
-
- i = (ij / 177) % 177 + 2;
- j = ij % 177 + 2;
- k = (kl / 169) % 178 + 1;
- l = kl % 169;
-
- for (ii = 1; ii <= LASTDIM - 1; ii++) {
- s = 0.0;
- t = 0.5;
- for (jj = 1; jj <= 24; jj++) {
- m = (((i * j) % 179) * k) % 179;
- i = j;
- j = k;
- k = m;
- l = (53 * l + 1) % 169;
- if ((l * m) % 64 >= 32)
- s += t;
- t *= 0.5;
- }
- x[ii] = (long) floor(s * NO_NUMBERS);
- }
-
- c = 362436;
-
- k_ptr = &x[FIRSTDIM - 1];
- j_ptr = &x[LASTDIM - 1];
- }
- #endif
-
-